package net.sf.rose.file.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import net.sf.rose.util.RoseRuntimeException;

public class JavaUtils {

    private static Logger LOG = Logger.getLogger(JavaUtils.class);

    /** 自动编绎 */
    public static boolean autoCompile = false;

    /**
     * 根据指定的Java文件获取它的类名
     * @param file - 指定的文件，可以是java源文件或class文件
     * @return java类名
     */
    public static String getClassNameByFilePath(String path) {
        String rootName = null;
        // java源文件
        if (path.endsWith(".java")) {
            rootName = FileUtils.JAVA_SOURCE_PATH.getName();
        }
        // class文件
        else {
            rootName = FileUtils.JAVA_CLASS_PATH.getName();
        }
        path = path.substring(0, path.length() - 5);
        path = path.substring(path.indexOf(rootName) + rootName.length() + 1);
        path = StringUtils.replaceChars(path, '\\', '.');
        path = StringUtils.replaceChars(path, '/', '.');
        return path;
    }

    /**
     * 将源class文件夹路径中的所有文件打包生成目标jar文件
     * @param file - 目标jar文件
     * @param path - 源class文件夹路径
     */
    public static File getFileByClassName(String className) {
        File file = null;
        // 如果是Java文件
        if (className.endsWith(".java")) {
            file = FileUtils.JAVA_SOURCE_PATH;
        }
        // 如果是Html文件
        else{
            file = FileUtils.WEB_APP_PATH;
            className = className.replace('\\', '.');
            className = className.replace('/', '.');
        }
        String[] arr = StringUtils.split(className, '.');
        int len = arr.length;
        for (int i = 0; i < len - 2; i++) {
            file = new File(file, arr[i]);
            if (!file.exists()) {
                file.mkdir();
            }
        }
        file = new File(file, arr[len - 2] + '.' + arr[len - 1]);
        return file;
    }

    /**
     * 将java源代码字符串保存至工程文件的java source目录下，路径根据解析正文中的package获取
     * @param file - 指定的文件
     * @param code - 源代码字符串
     * @return java源文件的存储全路径
     */
    public static File saveFile(File file, String code) {
        File path = FileUtils.JAVA_SOURCE_PATH;
        try {
            // 读取Java源文件中的包名称
            String str = code.substring(0, code.indexOf(';'));
            str = str.substring(str.indexOf(' '));
            str = str.trim();
            // 将包名转化为文件路径
            if (str.indexOf('.') != -1) {
                String[] arr = StringUtils.split(str, ".");
                for (int i = 0; i < arr.length; i++) {
                    path = new File(path, arr[i]);
                }
            } else {
                path = new File(path, str);
            }
            path = new File(path, file.getName());
            OutputStreamWriter op = new OutputStreamWriter(new FileOutputStream(path), "utf-8");
            op.append(code);
            op.flush();
            op.close();
        } catch (Exception e) {
            String str = "Java源代码保存失败！\n" + e.getMessage();
            throw new RoseRuntimeException(str + e);
        }
        return path;
    }

    /**
     * 重新编绎指定的java源代码 调用此方法必须要将JDK的dt.jar,tool.jar加进
     * @param javaFile - java源文件存放路径
     */
    public static void comareJavaFile(File javaFile) {
        // 如果不需要自动编绎
        if (!autoCompile) {
            return;
        }
        if (!javaFile.getName().endsWith(".java")) {
            return;
        }
        try {
            JavaCompiler cm = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager sm = cm.getStandardFileManager(null, null, null);
            Iterable<? extends JavaFileObject> javaFileObjects = sm.getJavaFileObjects(javaFile);
            File distDir = FileUtils.JAVA_CLASS_PATH;
            // 设置编绎路径和编码格式 -encoding utf-8
            List<String> options = Arrays.asList("-d", distDir.getAbsolutePath(), "-encoding", "utf-8");
            JavaCompiler.CompilationTask task = cm.getTask(null, sm, null, options, null, javaFileObjects);
            task.call();
            sm.close();
        } catch (Exception e) {
            String str = "class文件：" + javaFile.getAbsolutePath();
            str = str + "编绎失败,调用此方法必须要将JDK的dt.jar,tool.jar加入lib库！\n" + e.getMessage();
            LOG.error(str, e);
            throw new RoseRuntimeException(str, e);
        }
    }

    /**
     * 将源class文件夹路径中的所有文件打包生成目标jar文件
     * @param file - 目标jar文件
     * @param path - 源class文件夹路径
     */
    public static void toJarFile(File file, File path) {
        List<File> list = new ArrayList<>();
        getFileList(path, list);
        toJarFile(file, path, list);
    }

    /**
     * 将源class文件夹路径中的指定的部分文件打包生成目标jar文件
     * @param file - 目标jar文件
     * @param path - 源class文件夹路径
     * @param list - 指定部分文件
     */
    public static void toJarFile(File file, File path, List<File> list) {
        try {
            String pathName = path.getAbsolutePath();
            int pathLength = pathName.length();
            File tmpJarFile = File.createTempFile("tempJar", ".tmp");
            JarOutputStream tempStream = new JarOutputStream(new FileOutputStream(tmpJarFile));
            List<String> fileNames = new ArrayList<String>();
            // Added the new files to the jar.
            for (int i = 0; i < list.size(); i++) {
                File f = list.get(i);
                FileInputStream fis = new FileInputStream(f);
                byte[] buffer = new byte[1024];
                int bytesRead = 0;
                System.out.println(f.getName());
                System.out.println(f.getAbsolutePath().substring(pathLength));
                JarEntry entry = new JarEntry(f.getAbsolutePath().substring(pathLength));
                fileNames.add(entry.getName());
                tempStream.putNextEntry(entry);
                while ((bytesRead = fis.read(buffer)) != -1) {
                    tempStream.write(buffer, 0, bytesRead);
                }
                fis.close();
            }
            String strSep = System.getProperty("line.separator");
            StringBuilder sb = new StringBuilder();
            sb.append("Manifest-Version: 1.0").append(strSep);
            sb.append("Built-By: fengjian").append(strSep);
            sb.append("Class-Path: ").append(strSep);
            sb.append("Build-Jdk: 1.8.0_131").append(strSep);
            sb.append("License: 12345678").append(strSep);
            sb.append("Created-By: Apache Maven 3.6.0").append(strSep);
            sb.append("Archiver-Version: Plexus Archiver").append(strSep);
            byte[] buffer = sb.toString().getBytes();
            JarEntry entry = new JarEntry("META-INF\\MANIFEST.MF");
            fileNames.add(entry.getName());
            tempStream.putNextEntry(entry);
            tempStream.write(buffer, 0, buffer.length);

            // add the old files to the jar.
            if (file.exists()) {
                JarFile jarFile = new JarFile(file);
                // Copy original jar file to the temporary one.
                Enumeration<?> jarEntries = jarFile.entries();
                while (jarEntries.hasMoreElements()) {
                    entry = (JarEntry) jarEntries.nextElement();
                    if (!exists(entry.getName(), fileNames)) {
                        InputStream entryInputStream = jarFile.getInputStream(entry);
                        tempStream.putNextEntry(entry);
                        buffer = new byte[1024];
                        int bytesRead = 0;
                        while ((bytesRead = entryInputStream.read(buffer)) != -1) {
                            tempStream.write(buffer, 0, bytesRead);
                        }
                    }
                }
                jarFile.close();
                file.delete();
            }
            tempStream.close();
            tmpJarFile.renameTo(file);
        } catch (Exception ex) {
            throw new RoseRuntimeException(ex);
        }
    }

    /**
     * 判断字符串在列表中是否存在
     * @param name - 字符串
     * @param list - 列表
     * @return
     */
    private static boolean exists(String name, List<String> list) {
        for (String s : list) {
            if (s.equals(name)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 递归遍历某个目录，将所有文件存储在一个列表文件中
     * @param path - 文件目录
     * @param list - 文件列表
     */
    private static void getFileList(File path, List<File> list) {
        for (File f : path.listFiles()) {
            if (f.isDirectory()) {
                getFileList(f, list);
            } else {
                list.add(f);
            }
        }
    }

    /**
     * 测试
     * @param args - 参数
     */
    public static void main(String[] args) {
        File file = new File("e://rose-boot-1.0.2.jar");
        File f = new File("C:\\12.vscode\\guard\\target\\classes\\com\\baidu\\ueditor\\PathFormat.class");
        List<File> list = new ArrayList<>();
        list.add(f);
        // toJarFile(file, new File("C:\\12.vscode\\guard\\target\\classes"), list);
        toJarFile(file, new File("C:\\12.vscode\\guard\\target\\classes"));
    }
}